package cn.edu.cug.cs.gtl.docsrv.raw.manager;

import cn.edu.cug.cs.gtl.docsrv.raw.catalog.CatalogTree;
import cn.edu.cug.cs.gtl.mybatis.BatisWrapper;
import cn.edu.cug.cs.gtl.mybatis.Session;
import cn.edu.cug.cs.gtl.mybatis.mapper.raw.*;
import cn.edu.cug.cs.gtl.protos.TimeValue;

import java.io.File;
import java.math.BigDecimal;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public abstract class RawManager {
    protected Session session;//开启数据库会话

    /**
     * 构造函数
     * @param session
     */
    public RawManager(Session session){
        this.session = session;
    }

    /**
     * 设置数据库会话
     * @param s
     */
    public void setSession(Session s){
        this.session=s;
    }

    /**
     * 将一个由rawItem+rawFile组成的原始文档插入数据库
     * @param rawItem 原始元数据信息
     * @param rawFile 原始文件内容
     * @return
     */
    public abstract boolean insert(RawItem rawItem, RawFile rawFile);

    /**
     * 将一个由RawAll组成的原始文档插入数据库
     * @param rawAll 原始文档
     * @return 成功返回true,否则返回false
     */
    public abstract boolean insert(RawAll rawAll);

    /**
     * 将一个由RawCatalog插入数据库
     * @param rawCatalog 原始文档
     * @return
     */
    public abstract void insert(RawCatalog rawCatalog);

    /**
     * 读取本地目录中的所有原始文档，将其插入原始库中
     * @param localDir 本地目录
     * @return 成功返回true,否则返回false
     */
    public abstract boolean insert(String localDir);

    /**
     * 根据rawInternalId删除数据库中对应的原始文档记录
     * @param rawItem 必须填充rawInternalId
     * @return
     */
    public abstract boolean delete(RawItem rawItem);

    /**
     * 根据rawInternalId删除数据库中对应的原始文档记录
     * @param rawAll 必须填充rawInternalId
     * @return
     */
    public abstract boolean delete(RawAll rawAll);

    /**
     * 根据rawItem+rawFile更新数据库中的原始文档记录
     * @param rawItem 该对象的rawInternalId必须填充
     * @param rawFile 更新文档文件的内容
     * @return 更新成功返回true,否则返回false
     */
    public abstract boolean update(RawItem rawItem, RawFile rawFile);

    /**
     * 根据根据rawAll中的信息更新数据库中的记录
     * @param rawAll 该对象的rawInternalId必须填充
     * @return 更新成功返回true,否则返回false
     */
    public abstract boolean update(RawAll rawAll);
 
    /**
     * 根据rawInternalId查询文档
     * @param rawInternalId
     * @return RawAll
     */
    public abstract RawAll queryByInternalId(BigDecimal rawInternalId);

    public abstract List<RawItem> queryItemByInternalId(BigDecimal rawInternalId);

    /**
     * 根据文档标题查询文档
     * @param rawEnTitle
     * @return List<rawAll>
     */
    public abstract List<RawAll> queryByEnTitle(String rawEnTitle);

    /**
     * 根据文档注释查询文档
     * @param rawCnTitle  文档注释
     * @return
     */
    public abstract List<RawAll> queryByComment(String rawCnTitle);

    /**
     * 根据文档类型查询文档
     * @param rawType
     * @return
     */
    public abstract List<RawAll> queryByType(String rawType);

    /**
     * 从文档库中提取文档添加时间段为[begin,end)之内的文档
     * @param begin
     * @param end
     * @return
     */
    public abstract List<RawAll> queryByAppendDate(TimeValue begin, TimeValue end);

 
    /**
     * 根据传入参数在原始库中进行查询
     * {
     *     rawInternalId:0
     *     rawEnTitle:"tomcat"
     *     rawCnTitle:"汤姆猫"
     * }
     * @param parameters
     *     Type(Object)  Name(String)
     *     BigDecimal rawInternalId;
     *
     *     String rawEnTitle;
     *
     *     String rawCnTitle;
     *
     *     String rawUuid;
     *
     *     String rawVersion;
     *
     *     String rawKeywords;
     *
     *     String rawAbstract;
     *
     *     String rawContent;
     *
     *     String rawType;
     *
     *     String rawAuthors;
     *
     *     String rawAffiliation;
     *
     *     String rawTags;
     *
     *     String rawUrl;
     *
     *     Date rawAppendDate;
     *
     *     Date rawUpdateDate;
     *
     *     Date rawCreateDate;
     *
     *     String rawMemo;
     *
     *     BigDecimal rawCatalog;
     *
     *     String rawExtent;
     *
     * @return 返回查询到的文档。
     */
    public  abstract List<RawAll> query(Map<String,Object> parameters);

    /**
     * 获取原始文档的目录列表
     * @return
     */
    public List<RawCatalog> getCatalogs(){
        if(session.isClosed())
            return null;
        RawCatalogMapper rawMapper = session.getMapper(RawCatalogMapper.class);
        List<RawCatalog> ls = rawMapper.selectAll();
        if(ls==null || ls.size()==0) return null;
        return ls;
    }

    /**
     * 如果catalogName 为 中国/湖北/武汉,则返回的是一个字符串对象的数组，
     * 含有三个元素中国、湖北、武汉，表示三级分类
     * @param catalogInternalId
     * @return
     */
    public String[] getCatalogName(BigDecimal catalogInternalId){
        if(session.isClosed())
            return null;
        RawCatalogMapper rawMapper = session.getMapper(RawCatalogMapper.class);
        RawCatalog ls = rawMapper.selectByPrimaryKey(catalogInternalId);
        String c = ls.getCatalogName();
        if(c==null || c.isEmpty()) return null;
        if(!c.contains("/")){
            String [] ss = new String[1];
            ss[0]=c;
            return ss;
        }
        else{
            return c.split("/");
        }
    }

    /**
     * CatalogOrder用于对分类名称进行排序，以便在前端可以按照这个设置进行显示排序
     * @return
     */
    public String getCatalogOrder(BigDecimal catalogInternalId){
        if(session.isClosed())
            return null;
        RawCatalogMapper rawMapper = session.getMapper(RawCatalogMapper.class);
        RawCatalog ls = rawMapper.selectByPrimaryKey(catalogInternalId);
        return ls.getCatalogOrder();
    }

    /**
     * 根据指定的本地目录，
     * 获取其下的所有子目录，
     * 构建RawCatalog
     * 例如：传入的参数是/Users/ZhenwenHe/git/data
     * 在该目录下的data目录结构如下：
     *                         spatialdata/wuhan
     *                         spatialdata/beijing
     *                         imagedata/wuhan
     *                         imagedata/beijing
     * 则会生成7个RawCatalog,其name分别是：
     *                         data
     *                         data/spatialdata
     *                         data/imagedata
     *                         data/spatialdata/wuhan
     *                         data/spatialdata/beijing
     *                         data/imagedata/wuhan
     *                         data/imagedata/beijing
     * 然后会和RAW_CATALOG中的数据进行比较，
     * 如果存在，则修改其ID和ORDER
     * 如果不存在，则插入RAW_CATALOG中
     * @param localDir
     * @return 返回数据库中，当前目录下所有合格的分类对象
     */
    public List<RawCatalog> localDirToCatalogs(String localDir ){
        try {
            cn.edu.cug.cs.gtl.io.File f = new cn.edu.cug.cs.gtl.io.File(localDir);
            if(!f.exists()) return null;

            if(session.isClosed())
                session= BatisWrapper.getInstance().getMasterSession();

            RawCatalogMapper mapper = session.getMapper(RawCatalogMapper.class);
            if(mapper==null) return null;

            List<RawCatalog> ls= new ArrayList<>();
            dirToCatalogs(null,localDir, ls);

            //去除数据库中已经存在的分类，并赋值相应的ID
            BigDecimal id = mapper.maxInternalId();
            id = id.add(BigDecimal.ONE);
            List<RawCatalog> existCatalogs = mapper.selectAll();
            boolean exist = false;
            for(RawCatalog i: ls){
                for(RawCatalog j : existCatalogs){
                    exist=false;
                    //如果存在，则直接拷贝已经存在的RawCatalog
                    if(i.getCatalogName().equals(j.getCatalogName())){
                        i.setCatalogInternalId(j.getCatalogInternalId());
                        if(j.getCatalogOrder()!=null)
                            i.setCatalogOrder(j.getCatalogOrder());
                        exist=true;
                        break;
                    }
                }
                if(exist==false){//如果不存在，插入数据库
                    i.setCatalogInternalId(id);
                    id = id.add(BigDecimal.ONE);
                    mapper.insert(i);
                }
                else{//存在
                    continue;
                }
            }
            session.commit();
            return ls;
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 采用递归方法，只供List<RawCatalog> dirToCatalogs(String localDir )调用
     * @param parentCatalogName
     * @param localDir
     * @param ls
     * @throws Exception
     */
    private   static void dirToCatalogs(
            String parentCatalogName,
            String localDir,
            List<RawCatalog>ls) throws Exception{

        cn.edu.cug.cs.gtl.io.File f = new cn.edu.cug.cs.gtl.io.File(localDir);
        if(!f.exists()) return;

        if(f.isDirectory()){
            String [] ss = cn.edu.cug.cs.gtl.io.File.split(localDir);
            RawCatalog rawCatalog = new RawCatalog();
            //rawCatalog.setCatalogInternalId(rawCatalogMapper.maxInternalId().add(BigDecimal.ONE));
            if(parentCatalogName==null || parentCatalogName.isEmpty()) {
                parentCatalogName = ss[ss.length - 1];
                rawCatalog.setCatalogName(parentCatalogName);
            }
            else {
                parentCatalogName=parentCatalogName + File.separator + ss[ss.length - 1];
                rawCatalog.setCatalogName(parentCatalogName);
            }
            ls.add(rawCatalog);
            for(File fo: f.listFiles()){
                if(!fo.isDirectory())
                    continue;
                dirToCatalogs(parentCatalogName,fo.getAbsolutePath(),ls);
            }
            return;
        }
        else {
            return;
        }
    }
    /**
     * 获取分类目录树
     * @return
     */
    public CatalogTree getCatalogTree()  {
        try {
            List<RawCatalog> rawCatalogs =getCatalogs();
            DatabaseMetaData metaData = session.getDataSource().getConnection().getMetaData();
            if(metaData!=null )
                return new CatalogTree(rawCatalogs,metaData.getURL());
            else
                return new CatalogTree(rawCatalogs);
        }
        catch (SQLException e){
            e.printStackTrace();
        }
        return null;
    }

}
